﻿using Project3D.my3D.Structure;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;

namespace Project3D.my3D.File3D
{

    public class OBJ_File
    {
        public static bool read(String filename, myMesh mesh)
        {
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-EN");

            try
            {
                int i, j;
                int n, n2;
                int jp, jm;
                myVertex vertex;
                myFace face;
                myHalfedge halfedge = null;
                Dictionary<int, myHalfedge> halfedgeTable = new Dictionary<int, myHalfedge>();
                int twinKey, twinId1, twinId2;
                int f_id = 0, e_id = 0, v_id = 1;

                mesh.vertices.Clear();
                mesh.faces.Clear();
                mesh.halfedges.Clear();

                if (!System.IO.File.Exists(filename))
                {
                    Console.WriteLine("Sorry, unable to read file.");
                    return false;
                }
                System.IO.FileInfo f = new System.IO.FileInfo(filename);
                string[] data = System.IO.File.ReadAllLines(filename);

                for (i = 0; i < data.Length; i++)
                {
                    string[] items = System.Text.RegularExpressions.Regex.Split(data[i], @"\s+");
                    n = items.Length;
                    //In case there are spaces at the end of a line. Then don't consider the last empty item.
                    if (items[n - 1].Length == 0) n--;

                    for (j = 0; j < n; j++)
                    {
                        int endc = items[j].IndexOf('/');
                        if (endc != -1) items[j] = items[j].Substring(0, endc);
                    }


                    if (items[0] == "v")
                    {
                        vertex = new myVertex();
                        vertex.point.X = double.Parse(items[1]);
                        vertex.point.Y = double.Parse(items[2]);
                        vertex.point.Z = double.Parse(items[3]);
                        vertex.v_id = v_id;
                        v_id++;
                        mesh.vertices.Add(vertex);
                    }
                    else if (items[0] == "f")
                    {
                        face = new myFace();
                        face.f_id = f_id;
                        f_id++;
                        mesh.faces.Add(face);

                        n2 = n - 1;
                        myHalfedge[] halfedgeOfFace = new myHalfedge[n2];
                        for (j = 0; j < n2; j++)
                        {
                            halfedgeOfFace[j] = new myHalfedge();
                            halfedgeOfFace[j].e_id = e_id;
                            e_id++;
                        }

                        face.adjacent_halfedge = halfedgeOfFace[0];

                        for (j = 1; j < n; j++)
                        {
                            jp = (j) % n2;
                            jm = (n2 + j - 2) % n2;

                            halfedge = halfedgeOfFace[j - 1];

                            vertex = mesh.vertices[int.Parse(items[j]) - 1];
                            halfedge.source = vertex;
                            if (vertex.originof_halfedge == null)
                                vertex.originof_halfedge = halfedge;

                            halfedge.adjacent_face = face;

                            halfedge.prev = halfedgeOfFace[jm];
                            halfedge.next = halfedgeOfFace[jp];

                            twinId1 = halfedge.source.v_id;
                            twinId2 = int.Parse(items[j % n2 + 1]);
                            twinKey = Math.Min(twinId1, twinId2) * mesh.vertices.Count + Math.Max(twinId1, twinId2);

                            if (halfedgeTable.ContainsKey(twinKey))
                            {
                                halfedge.twin = halfedgeTable[twinKey];
                                halfedgeTable[twinKey].twin = halfedge;
                            }
                            else
                            {
                                halfedgeTable.Add(twinKey, halfedge);
                            }

                            mesh.halfedges.Add(halfedge);
                        }
                    }
                    else
                    {
                        //A different character on the file line. Ignoring it.
                    }
                }
            }
            catch
            {
                return false;
            }

            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("fr-FR");
            return true;
        }


        public static void write(String filename, myMesh mesh, bool triangular)
        {
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-EN");

            try
            {
                String resultFile = "";
                int i = 1;

                for (i = 0; i < mesh.vertices.Count; i++)
                    mesh.vertices[i].v_id = i;

                foreach (myVertex _vertex in mesh.vertices)
                    resultFile += "v " + _vertex.point.X + " " + _vertex.point.Y + " " + _vertex.point.Z + Environment.NewLine;


                if (triangular)
                    resultFile += OBJ_File.writeTriangularFace(mesh);
                else
                    resultFile += OBJ_File.writeFace(mesh);

                System.IO.File.WriteAllText(filename, resultFile, System.Text.Encoding.ASCII);
            }
            catch
            { }

            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("fr-FR");
        }

        private static String writeFace(myMesh mesh)
        {
            String result = "";
            myHalfedge edge;

            foreach (myFace _face in mesh.faces)
            {
                result += "f ";
                edge = _face.adjacent_halfedge;

                do
                {
                    result += (edge.source.v_id + 1) + " ";
                    edge = edge.next;
                } while (edge.e_id != _face.adjacent_halfedge.e_id);

                result += Environment.NewLine;
            }

            return result;
        }

        private static String writeTriangularFace(myMesh mesh)
        {
            String result = "";
            myHalfedge edge;
            myVertex firstvertex;

            foreach (myFace _face in mesh.faces)
            {
                firstvertex = _face.adjacent_halfedge.source;
                edge = _face.adjacent_halfedge.next;

                do
                {
                    result += "f " + (firstvertex.v_id + 1) + " " + (edge.source.v_id + 1) + " " + (edge.next.source.v_id + 1) + Environment.NewLine;

                    edge = edge.next;
                } while (edge.next.e_id != _face.adjacent_halfedge.e_id);
            }

            return result;
        }

    }

}
